/*
 * Copyright 2013-2019 Xia Jun(3979434@qq.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************************************
 *                                                                                     *
 *                        Website : http://www.farsunset.com                           *
 *                                                                                     *
 ***************************************************************************************
 */
package com.workcell.workdata.im.service.impl;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.workcell.workdata.im.component.event.GroupEvent;
import com.workcell.workdata.im.component.holder.UidHolder;
import com.workcell.workdata.im.component.redis.GroupRedisTemplate;
import com.workcell.workdata.im.constant.MessageAction;
import com.workcell.workdata.im.entity.EventMessage;
import com.workcell.workdata.im.entity.Group;
import com.workcell.workdata.im.entity.GroupMember;
import com.workcell.workdata.im.entity.Message;
import com.workcell.workdata.im.model.request.GroupCreateRequest;
import com.workcell.workdata.im.model.request.GroupInviteRequest;
import com.workcell.workdata.im.model.vo.GroupMemberVO;
import com.workcell.workdata.im.model.vo.GroupRobotVO;
import com.workcell.workdata.im.model.vo.GroupVO;
import com.workcell.workdata.im.repository.GroupMessageRepository;
import com.workcell.workdata.im.repository.GroupRepository;
import com.workcell.workdata.im.service.GroupMemberService;
import com.workcell.workdata.im.service.GroupRobotService;
import com.workcell.workdata.im.service.GroupService;
import com.workcell.workdata.im.service.UserService;
import com.workcell.workdata.im.util.JSON;
import com.workcell.workdata.im.util.PageX;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Service
			public class GroupServiceImpl implements GroupService{

	@Resource
	private GroupRepository groupRepository;

	@Resource
	private GroupMemberService groupMemberService;

	@Resource
	private GroupRobotService groupRobotService;

	@Resource
	private GroupMessageRepository groupMessageRepository;

	@Resource
	private GroupRedisTemplate groupRedisTemplate;

	@Resource
	private ApplicationContext applicationContext;

	@Resource
	private UserService userService;

	private final LoadingCache<Long, String> nameCache;

	@Autowired
	public GroupServiceImpl(){
		nameCache = CacheBuilder.newBuilder()
				.maximumSize(200)
				.expireAfterAccess(1, TimeUnit.HOURS)
				.build(CacheLoader.from(id -> groupRepository.findName(id)));
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Group create(GroupCreateRequest request, Long uid) {

		Group group = new Group();
		group.setUid(uid);
		group.setName(request.getName());
		group.setNotice(request.getNotice());
		group.setCreateTime(new Date());
		group.setState(Group.STATE_NORMAL);
		groupRepository.saveAndFlush(group);
		groupRedisTemplate.save(group);


		GroupMember founder = new GroupMember();
		founder.setUid(uid);
		founder.setGroupId(group.getId());
		founder.setType(GroupMember.TYPE_FOUNDER);

		groupMemberService.add(founder);

		groupMemberService.add(request.getIdList(),group.getId());

		Message message = new EventMessage();
		message.setAction(MessageAction.ACTION_300);
		message.setSender(group.getId());
		message.setExtra(uid.toString());

		GroupEvent event = new GroupEvent(message,uid);
		event.setUidList(request.getIdList());

		applicationContext.publishEvent(event);

		return group;
	}


	@Override
	public List<GroupVO> getRelevantList(Long uid) {


		List<Long> idList = groupRepository.findIdList(uid);

		if (CollectionUtils.isEmpty(idList)){
			return Collections.emptyList();
		}

		List<GroupVO> voList = PageX.map(GroupVO::of,groupRepository.findList(idList));

		List<GroupRobotVO> robots = groupRobotService.findList(idList);

		List<GroupMemberVO> members = groupMemberService.findList(idList);

		for (GroupVO group : voList){
			group.setMemberList(members.stream().filter(member -> Objects.equals(group.getId(), member.getGroupId())).collect(Collectors.toList()));
			group.setRobotList(robots.stream().filter(robot -> Objects.equals(group.getId(), robot.getGroupId())).collect(Collectors.toList()));
		}

		return voList;
	}

	@Override
	public GroupVO findOne(long id) {
		return findOne(id,true);
	}

	@Override
	public GroupVO findOne(long id, boolean loadMember) {
		AtomicReference<Group> group = new AtomicReference<>();

		//group.set(groupRedisTemplate.get(id));

		if (group.get() == null) {
			group.set(groupRepository.findById(id).orElse(null));
		}

		if (group.get() == null){
			return null;
		}

		groupRedisTemplate.save(group.get());


		GroupVO vo = GroupVO.of(group.get());
		if (loadMember){
			vo.setMemberList(groupMemberService.findList(id));
			vo.setRobotList(groupRobotService.findList(id));
		}

		return vo;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void delete(long id) {

		GroupVO group = findOne(id,false);

		Message message = new EventMessage();
		message.setSender(id);
		message.setAction(MessageAction.ACTION_303);
		message.setExtra(UidHolder.getUid().toString());

		GroupEvent event = new GroupEvent(message,group.getUid());
		event.addIgnored(group.getUid());
		event.setUidList(groupMemberService.findUidList(id));
		applicationContext.publishEvent(event);

		groupRepository.deleteById(id);
		groupMemberService.remove(id);
		groupRobotService.remove(id);

		groupMessageRepository.deleteAll(id);

		groupRedisTemplate.remove(id);

	}


	@Override
	@Transactional(rollbackFor = Exception.class)
	public void block(Long id) {

		groupRepository.updateState(id,Group.STATE_BLOCKED);
		groupRedisTemplate.remove(id);

		long uid = UidHolder.getUid();

		Message message = new EventMessage();
		message.setSender(id);
		message.setExtra(String.valueOf(uid));
		message.setAction(MessageAction.ACTION_308);

		GroupEvent groupEvent = new GroupEvent(message,uid);
		groupEvent.addIgnored(uid);
		applicationContext.publishEvent(groupEvent);

	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void unblock(Long id) {

		groupRepository.updateState(id,Group.STATE_NORMAL);
		groupRedisTemplate.remove(id);

		long uid = UidHolder.getUid();

		Message message = new EventMessage();
		message.setSender(id);
		message.setExtra(String.valueOf(uid));
		message.setAction(MessageAction.ACTION_309);

		GroupEvent groupEvent = new GroupEvent(message,uid);
		groupEvent.addIgnored(uid);
		applicationContext.publishEvent(groupEvent);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void invite(GroupInviteRequest request, Long uid) {

		groupMemberService.add(request.getIdList(),request.getGroupId());

		Message message = new EventMessage();
		message.setAction(MessageAction.ACTION_300);
		message.setSender(request.getGroupId());
		message.setExtra(uid.toString());

		GroupEvent event300 = new GroupEvent(message,uid);
		event300.setUidList(request.getIdList());
		event300.setReplaceable(false);
		applicationContext.publishEvent(event300);

		Map<Long,String> nameMap = userService.findName(request.getIdList());

		Message inviteMessage = new EventMessage();
		inviteMessage.setAction(MessageAction.ACTION_311);
		inviteMessage.setSender(request.getGroupId());
		inviteMessage.setContent(JSON.toJSONString(nameMap));
		inviteMessage.setExtra(uid.toString());

		GroupEvent event311 = new GroupEvent(inviteMessage,uid);
		event311.addIgnored(request.getIdList());
		event311.addIgnored(uid);
		event311.setReplaceable(false);
		applicationContext.publishEvent(event311);

	}

	@Override
	public String getName(long id) {
		return nameCache.getUnchecked(id);
	}

	@Override
	public void updateGroupPhoto(Group group) {
		Group groupById = groupRepository.findGroupById(group.getId());

		if(StringUtils.isNotBlank(group.getPhoto())){
			groupById.setPhoto(group.getPhoto());
		}
		if(StringUtils.isNotBlank(group.getSummary())){
			groupById.setSummary(group.getSummary());
		}
		groupRepository.saveAndFlush(groupById);
		groupRedisTemplate.remove(groupById.getId());
	}


	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateName(long id, String name) {

		groupRepository.updateName(id,name);
		groupRedisTemplate.remove(id);

		long uid = UidHolder.getUid();

		Message message = new EventMessage();
		message.setSender(id);
		message.setExtra(String.valueOf(uid));
		message.setAction(MessageAction.ACTION_304);
		message.setContent(name);

		GroupEvent groupEvent = new GroupEvent(message, uid);
		groupEvent.addIgnored(uid);

		applicationContext.publishEvent(groupEvent);

	}


	@Override
	@Transactional(rollbackFor = Exception.class)
	public void transfer(long id,long uid) {

		Long createUid = groupRepository.findUid(id);

		groupRepository.updateUid(id,uid);

		groupRedisTemplate.remove(id);

		groupMemberService.updateType(id,createUid,GroupMember.TYPE_NORMAL);

		groupMemberService.updateType(id,uid,GroupMember.TYPE_FOUNDER);

		Message message = new EventMessage();
		message.setSender(id);
		message.setAction(MessageAction.ACTION_307);
		message.setContent(String.valueOf(uid));

		long currentUid = UidHolder.getUid();

		GroupEvent groupEvent = new GroupEvent(message, currentUid);
		groupEvent.addIgnored(currentUid);

		applicationContext.publishEvent(groupEvent);

	}


}
